//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ManagementStudioServices.Interfaces;
using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
using Microsoft.Research.ScientificWorkflow.TridentUtilities;
using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
using Microsoft.Practices.Unity;
using SR = Microsoft.Research.DataLayer;
using System;

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.WorkflowManagerModule
{
    /// <summary>
    /// The Presentation logic for the deleted workflows table.
    /// </summary>
    public class DeletedWorkflowsListPresenter : IListPresenter, INotifyPropertyChanged
    {
        #region Private variables
        /// <summary>
        /// The unity container instance.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// The workflow manager service instance.
        /// </summary>
        private IWorkflowManagerService workflowManagerService;

        /// <summary>
        /// The search view for the available workflows filter.
        /// </summary>
        private UIElement searchView;

        /// <summary>
        /// The Title on the tableview.
        /// </summary>
        private string listViewTitle;

        /// <summary>
        /// The view which displays the workflows.
        /// </summary>
        private UIElement deletedWorkflowsListView;

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        private Collection<MenuItem> contextMenuItems;

        /// <summary>
        /// The selected index of the listview.
        /// </summary>
        private int selectedIndex;

        /// <summary>
        /// Flag to check if items were retreived in the query. 
        /// </summary>
        private bool emptyList;

        /// <summary>
        /// The items to be displayed on the tableview.
        /// </summary>
        private ObservableCollection<object> items;


        /// <summary>
        /// The command executed when the user selects a workflow.
        /// </summary>
        public DelegateCommand<WorkflowViewModel> WorkflowSelectChangedCommand { get; private set; }

        /// <summary>
        /// Holds the current filter.
        /// </summary>
        private string currentFilter;

        /// <summary>
        /// Holds the selectedWorkflow
        /// </summary>
        private WorkflowViewModel selectedWorkflow; 

        private string workflowName;
        private string deletedBy;

        #endregion

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="container">The unity contaier</param>
        /// <param name="view">The center pane view of the presenter.</param>
        /// <param name="service">The workflow manager service</param>
        public DeletedWorkflowsListPresenter(IUnityContainer container, DeletedWorkflowsListView view, IWorkflowManagerService service)
        {
            this.container = container;
            this.deletedWorkflowsListView = view;
            this.workflowManagerService = service;

            this.items = new ObservableCollection<object>();
            this.ListViewTitle = ManagementStudioResourceManager.GetString("TableTitleDeletedWorkflows");
            this.searchView = this.container.Resolve<DeletedWorkflowsSearchView>();
            (this.deletedWorkflowsListView as DeletedWorkflowsListView).DataContext = this;
            (this.searchView as DeletedWorkflowsSearchView).DataContext = this;
            this.Items.CollectionChanged += Items_CollectionChanged;

            // Initialize commands.
            this.RestoreCommand = new DelegateCommand<WorkflowViewModel>(this.ExecuteRestoreWorkflow, this.CanExecuteRestoreWorkflow);
            this.PurgeCommand = new DelegateCommand<WorkflowViewModel>(this.ExecutePurgeWorkflow, this.CanExecutePurgeWorkflow);
            this.RefreshListCommand = new DelegateCommand<object>(p => { this.DisplayItems(this.currentFilter); });
            this.WorkflowSelectChangedCommand = new DelegateCommand<WorkflowViewModel>(p => { this.selectedWorkflow = p; this.RaiseCanExecuteEvents(); });

            this.SearchCommand = new DelegateCommand<object>(p => this.SearchCommandExecute());
            
            this.SetContextMenuItems();
        }

        #region Public Properties

        /// <summary>
        /// The command executed when the search button is clicked.
        /// </summary>
        public ICommand SearchCommand { get; set; }

        /// <summary>
        /// The command executed when the restore button is clicked.
        /// </summary>
        public DelegateCommand<WorkflowViewModel> RestoreCommand { get; private set; }

        /// <summary>
        /// The command executed when the purge button is clicked.
        /// </summary>
        public DelegateCommand<WorkflowViewModel> PurgeCommand { get; private set; }

        /// <summary>
        /// The command to refresh the list.
        /// </summary>
        public DelegateCommand<object> RefreshListCommand { get; private set; }

        /// <summary>
        /// Property to check if items were retreived in the query.
        /// </summary>
        public bool EmptyList
        {
            get
            {
                return this.emptyList;
            }
            private set
            {
                this.emptyList = value;
                this.OnNotifyPropertyChanged("EmptyList");
            }
        }

        /// <summary>
        /// Workflow name search text property
        /// </summary>
        public string WorkflowName
        {
            get
            {
                return this.workflowName;
            }
            set
            {
                this.workflowName = value;
                this.OnNotifyPropertyChanged("WorkflowName");
            }
        }        


        /// <summary>
        /// Deleted by search text property
        /// </summary>
        public string DeletedBy
        {
            get
            {
                return this.deletedBy;
            }
            set
            {
                this.deletedBy= value;
                this.OnNotifyPropertyChanged("DeletedBy");
            }
        }        


        #endregion
        
        #region IListPresenter Members

        /// <summary>
        /// The items to be displayed in the table view.
        /// </summary>
        public ObservableCollection<object> Items
        {
            get
            {
                return this.items;
            }
        }

        /// <summary>
        /// The title of the table view.
        /// </summary>
        public string ListViewTitle
        {
            get
            {
                return this.listViewTitle;
            }
            set
            {
                this.listViewTitle = value;
                this.OnNotifyPropertyChanged("ListViewTitle");
            }
        }

        /// <summary>
        /// Display the items in the table view.
        /// </summary>
        /// <param name="queryString"></param>
        public void DisplayItems(string queryString)
        {
            try
            {
                Items.Clear();

                SR.Activity.ISearch.ISearchClause clause = null;
                if (!string.IsNullOrEmpty(queryString))
                {
                    this.currentFilter = queryString;
                    if (queryString.Equals(ManagementStudioControlsConstants.RECENTLYDELETEDWORKFLOWS, StringComparison.OrdinalIgnoreCase))
                    {
                        DateTime cutoffDate = DateTime.Today.AddDays(-5);
                        clause = SR.Activity.ISearch.UpdatedDate(SR.DateTimeField.Condition.AfterOrEqual, cutoffDate);
                    }
                    else if (queryString.Equals(ManagementStudioControlsConstants.WORKFLOWSDELETEDBYMEFILTERNAME, StringComparison.OrdinalIgnoreCase))
                    {
                        clause = SR.Activity.ISearch.UpdatedBy(SR.StringField.Condition.Equals, Environment.UserName);
                    }
                }

                // Get the required items.
                Collection<SR.Activity> workflowList = this.workflowManagerService.GetDeletedWorkflows(clause);

                // Set the selected Index. Show the empty message if no items found.
                if (workflowList != null && workflowList.Count > 0)
                {
                    this.EmptyList = false;

                    // Set status.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                        workflowList.Count.ToString(CultureInfo.CurrentUICulture)));

                    // Add the items to the list.
                    foreach (SR.Activity workflow in workflowList)
                    {
                        WorkflowViewModel workflowViewmodel = new WorkflowViewModel(workflow);
                        Items.Add(workflowViewmodel);
                    }
                    this.SelectedIndex = 0;
                }
                else
                {
                    // Set the empty list property to true. Shows the empty list message and disables the buttons.
                    this.EmptyList = true;
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        public Collection<MenuItem> MenuItemCollection
        {
            get
            {
                return this.contextMenuItems;
            }
        }


        /// <summary>
        ///  The search view for the available workflows filter.
        /// </summary>
        public UIElement SearchView
        {
            get
            {
                return this.searchView;
            }
        }

        /// <summary>
        /// The view which displays the workflows.
        /// </summary>
        public UIElement ListView
        {
            get
            {
                return this.deletedWorkflowsListView;
            }
        }

        /// <summary>
        /// The selected index of the list.
        /// </summary>
        public int SelectedIndex
        {
            get
            {
                return this.selectedIndex;
            }
            set
            {
                this.selectedIndex = value;
                this.OnNotifyPropertyChanged("SelectedIndex");
            }
        }


        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// The property changed event. Fired when a proprty value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Fires the property changed event.
        /// </summary>
        /// <param name="propertyName">Name of the property which has been modified.</param>
        private void OnNotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        #region Private methods

        
        /// <summary>
        /// Implementation for the search click button.
        /// </summary>
        private void SearchCommandExecute()
        {
            try
            {
                Items.Clear();

                // This is the place where the workflows are retrieved from the registry. The search call ideally goes here.
                // Get the required items.
                SR.Activity.ISearch.ISearchClause filter = null;
                List<SR.Activity.ISearch.ISearchClause> filterArray = new List<SR.Activity.ISearch.ISearchClause>();
                if (!string.IsNullOrEmpty(this.WorkflowName))
                {
                    filterArray.Add(SR.Activity.ISearch.Name(SR.StringField.Condition.Contains, this.WorkflowName));
                }
                if (!string.IsNullOrEmpty(this.DeletedBy))
                {
                    filterArray.Add(SR.Activity.ISearch.UpdatedBy(SR.StringField.Condition.Contains, this.DeletedBy));
                }
                if (filterArray.Count > 1)
                {
                    filter = SR.Activity.ISearch.AND(filterArray.ToArray());
                }
                else
                {
                    filter = filterArray.Count > 0 ? filterArray[0] : null;
                }

                Collection<SR.Activity> workflowList = workflowManagerService.GetDeletedWorkflows(filter);

                // Set the selected Index. Show the empty message if no items found.
                if (workflowList != null && workflowList.Count > 0)
                {
                    this.SelectedIndex = 0;
                    this.EmptyList = false;
                    
                    // Set status.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                        workflowList.Count.ToString(CultureInfo.CurrentUICulture)));

                    // Add the items to the list.
                    // As the Items property is a observable collection and is bound to the listview. Any changes here will be shown in the listview. 
                    foreach (SR.Activity workflow in workflowList)
                    {
                        WorkflowViewModel workflowViewmodel = new WorkflowViewModel(workflow);
                        Items.Add(workflowViewmodel);
                    }
                }
                else
                {
                    // Set the empty list property to true. Shows the empty list message and disables the buttons.
                    this.EmptyList = true;
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }
                this.RaiseCanExecuteEvents();
            }
            catch (SR.RecordNotFoundException)
            {
                // Set status.
                this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                    0.ToString(CultureInfo.CurrentUICulture)));
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }

        /// <summary>
        /// Restores a deleted workflow. Only valid for soft-deleted workflows.
        /// </summary>
        /// <param name="jobModel">The </param>
        private void ExecuteRestoreWorkflow(WorkflowViewModel workflowModel)
        {
            try
            {
                if (this.SelectedIndex >= 0 && this.Items.Count > 0)
                {
                    if (workflowModel != null)
                    {
                        this.workflowManagerService.RestoreWorkflow(workflowModel.WorkflowId);
                        this.RefreshListCommand.Execute(null);
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }


        /// <summary>
        /// Restores a deleted workflow. Only valid for soft-deleted workflows.
        /// </summary>
        /// <param name="jobModel">The </param>
        private bool CanExecuteRestoreWorkflow(WorkflowViewModel workflowModel)
        {
            if (this.SelectedIndex >= 0 && this.Items.Count >0)
            {
                if (workflowModel != null)
                {
                    if (TridentAuthentication.LoggedUserRole == UserRole.Admin || workflowModel.OwnerId.Equals(((SR.IObject)TridentAuthentication.LoggedInUser).ID))
                    {
                        return true;
                    }
                }
            }

            return false;
        }


        /// <summary>
        /// Delete a workflow permanently.
        /// </summary>
        /// <param name="jobModel"></param>
        private void ExecutePurgeWorkflow(WorkflowViewModel workflowModel)
        {
            try
            {
                if (workflowModel != null)
                {
                    // Get a confirmation from the user.
                    MessageBoxResult result = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(
                        ManagementStudioResourceManager.GetString("PurgeWorkflowConfirmation"));

                    if (result == MessageBoxResult.Yes)
                    {
                        this.workflowManagerService.PurgeWorkflow(workflowModel.WorkflowId);
                        this.RefreshListCommand.Execute(null);
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleKnownExceptionsInUI(ex, "Error while purge the workflow");                
            }
        }


        /// <summary>
        /// Delete a workflow permanently.
        /// </summary>
        /// <param name="jobModel"></param>
        private bool CanExecutePurgeWorkflow(WorkflowViewModel jobModel)
        {
            if (TridentAuthentication.LoggedUserRole == UserRole.Admin)
            {
                if (this.SelectedIndex >= 0 && this.Items.Count > 0)
                {
                    return true;
                }
            }
                return false;
        }

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        private void SetContextMenuItems()
        {
            this.contextMenuItems = new Collection<MenuItem>();

            MenuItem restoreMenuitem = new MenuItem();
            restoreMenuitem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderRestore");
            restoreMenuitem.Command = RestoreCommand;
            restoreMenuitem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/restore.png");
            this.contextMenuItems.Add(restoreMenuitem);

            MenuItem purgeMenuitem = new MenuItem();
            purgeMenuitem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderPurge");
            purgeMenuitem.Command = PurgeCommand;
            purgeMenuitem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/purge.png");
            this.contextMenuItems.Add(purgeMenuitem);
        }

        /// <summary>
        /// Raise the can execute events so that the UI elements can be enabled/disabled.
        /// </summary>
        private void RaiseCanExecuteEvents()
        {
            this.RestoreCommand.RaiseCanExecuteChanged();
            this.PurgeCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Event handler for the collection changed event on the Items collection.
        /// </summary>
        /// <param name="sender">The source.</param>
        /// <param name="e">Event arguments</param>
        void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            ICollection<object> collection = sender as ICollection<object>;

            // If collection is empty then disable the buttons.
            this.EmptyList = (collection.Count == 0);
            this.RaiseCanExecuteEvents();
        }
        #endregion
    }
}
